using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Web;
using System.Collections.ObjectModel;
using System.ComponentModel;
using AjaxControlToolkit;

namespace AfterEdge.Web.UI
{
    /// <summary>
    /// Represents a prompt displayed to a user based on a set of conditions
    /// </summary>
    [RequiredScript(typeof(CommonToolkitScripts))]
    public class Prompt : ScriptControl, INamingContainer, IPostBackEventHandler
    {
        private static readonly Object EventCommand;

        private String _message;
        private String _title;
        private ButtonOptions _buttonOptions = ButtonOptions.Custom;
        private PostBackTarget _postBackTarget = PostBackTarget.Prompt;

        private bool _modal;
        private bool _allowDrag = true;
        private String _backgroundCssClass;

        private bool _causesValidation = true;
        private String _validationGroup;
        private String _postBackUrl;

        private Style _titleStyle;
        private Style _bodyStyle;
        private Style _footerStyle;
        private Style _defaultButtonStyle;

        private Panel _dragPanel;
        private Label _titleLabel;
        private Label _messageLabel;

        private PromptButtonCollection _buttons;
        private PromptTriggerCollection _triggers;
        private Collection<IExtenderControl> _extenders;
        private PromptClientEvents _clientEvents;

        public event CommandEventHandler Command
        {
            add { base.Events.AddHandler(Prompt.EventCommand, value); }
            remove { base.Events.RemoveHandler(Prompt.EventCommand, value); }
        }

        static Prompt()
        {
            EventCommand = new Object();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:Prompt"/> class.
        /// </summary>
        public Prompt() { }

        #region Properties

        /// <summary>
        /// Gets the client events.
        /// </summary>
        /// <value>The client events.</value>
        [Category("Behavior")]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public PromptClientEvents ClientEvents
        {
            get
            {
                if (this._clientEvents == null)
                    this._clientEvents = new PromptClientEvents();

                return this._clientEvents;
            }
        }

        /// <summary>
        /// Gets or sets the message that will be displayed to the user within the prompt.
        /// </summary>
        /// <value>The message.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(false)]
        [Category("Data")]
        public String Message
        {
            get { return this._message; }
            set { this._message = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="T:Prompt"/> is modal.
        /// </summary>
        /// <value><c>true</c> if modal; otherwise, <c>false</c>.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(true)]
        [Category("Behavior")]
        public bool Modal
        {
            get { return this._modal; }
            set { this._modal = value; }
        }

        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(true)]
        [Category("Behavior")]
        public bool AllowDrag
        {
            get { return this._allowDrag; }
            set { this._allowDrag = value; }
        }

        /// <summary>
        /// Gets or sets the background CSS class used when the <see cref="T:Prompt"/> has been set to modal.
        /// </summary>
        /// <value>The background CSS class.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(true)]
        [Category("Appearance")]
        public String BackgroundCssClass
        {
            get { return this._backgroundCssClass; }
            set { this._backgroundCssClass = value; }
        }

        /// <summary>
        /// Gets or sets the header text that is displayed on the heading of the <see cref="T:Prompt"/>
        /// </summary>
        /// <value>The header text.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(true)]
        [Category("Data")]
        public String Title
        {
            get { return this._title; }
            set { this._title = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="T:Prompt"/> causes validation to run.
        /// </summary>
        /// <value><c>true</c> if [causes validation]; otherwise, <c>false</c>.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(false)]
        [Category("Behavior")]
        public bool CausesValidation
        {
            get { return this._causesValidation; }
            set { this._causesValidation = value; }
        }

        /// <summary>
        /// Gets or sets the validation group used when the control causes validation.
        /// </summary>
        /// <value>The validation group.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(false)]
        [Category("Behavior")]
        public String ValidationGroup
        {
            get { return this._validationGroup; }
            set { this._validationGroup = value; }
        }

        /// <summary>
        /// Gets or sets the post back URL that a postback will be sent to.
        /// </summary>
        /// <value>The post back URL.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(false)]
        [Category("Behavior")]
        public String PostBackUrl
        {
            get { return this._postBackUrl; }
            set { this._postBackUrl = value; }
        }

        /// <summary>
        /// Gets or sets the built in button configuations to be used.
        /// </summary>
        /// <value>The button options.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(false)]
        [Category("Behavior")]
        public ButtonOptions ButtonOptions
        {
            get { return this._buttonOptions; }
            set { this._buttonOptions = value; }
        }

        /// <summary>
        /// Gets or sets the post back target.
        /// </summary>
        /// <value>The post back target.</value>
        [PersistenceMode(PersistenceMode.Attribute)]
        [Themeable(false)]
        [Category("Behavior")]
        public PostBackTarget PostBackTarget
        {
            get { return this._postBackTarget; }
            set { this._postBackTarget = value; }
        }

        /// <summary>
        /// Gets the header style to be applied to the header of the <see cref="T:Prompt"/>.
        /// </summary>
        /// <value>The header style.</value>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Themeable(true)]
        [Category("Appearance")]
        public Style TitleStyle
        {
            get
            {
                if (this._titleStyle == null)
                    this._titleStyle = new Style();

                return this._titleStyle;
            }
        }

        /// <summary>
        /// Gets the body style to be applied to the body of the <see cref="T:Prompt"/>.
        /// </summary>
        /// <value>The body style.</value>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Themeable(true)]
        [Category("Appearance")]
        public Style BodyStyle
        {
            get
            {
                if (this._bodyStyle == null)
                    this._bodyStyle = new Style();

                return this._bodyStyle;
            }
        }

        /// <summary>
        /// Gets the footer style to be applied to the footer (Button Region) of the <see cref="T:Prompt"/>.
        /// </summary>
        /// <value>The footer style.</value>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Themeable(true)]
        [Category("Appearance")]
        public Style FooterStyle
        {
            get
            {
                if (this._footerStyle == null)
                    this._footerStyle = new Style();

                return this._footerStyle;
            }
        }

        /// <summary>
        /// Gets the default button style to be applied to all buttons within the <see cref="T:Prompt"/>.
        /// </summary>
        /// <value>The default button style.</value>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Themeable(true)]
        [Category("Appearance")]
        public Style DefaultButtonStyle
        {
            get
            {
                if (this._defaultButtonStyle == null)
                    this._defaultButtonStyle = new Style();

                return this._defaultButtonStyle;
            }
        }

        /// <summary>
        /// Gets the buttons available within the <see cref="T:Prompt"/>.
        /// </summary>
        /// <value>The buttons.</value>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Themeable(false)]
        [Category("Behavior")]
        public PromptButtonCollection Buttons
        {
            get
            {
                if (this._buttons == null)
                    this._buttons = new PromptButtonCollection();

                return this._buttons;
            }
        }

        /// <summary>
        /// Gets the triggers configured for the <see cref="T:Prompt"/>.
        /// </summary>
        /// <value>The triggers.</value>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Themeable(false)]
        [Category("Behavior")]
        public PromptTriggerCollection Triggers
        {
            get
            {
                if (this._triggers == null)
                    this._triggers = new PromptTriggerCollection();

                return this._triggers;
            }
        }

        /// <summary>
        /// Gets the parameters configured for the <see cref="T:Prompt"/>.
        /// </summary>
        /// <value>The parameters.</value>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Themeable(false)]
        private Collection<IExtenderControl> Extenders
        {
            get
            {
                if (this._extenders == null)
                    this._extenders = new Collection<IExtenderControl>();

                return this._extenders;
            }
        }

        protected override System.Web.UI.HtmlTextWriterTag TagKey
        {
            get { return System.Web.UI.HtmlTextWriterTag.Div; }
        }

        #endregion

        protected override void CreateChildControls()
        {
            this._dragPanel = new Panel();
            this._dragPanel.ID = "Handle";

            this.Controls.Add(this._dragPanel);

            this._titleLabel = new Label();
            this._titleLabel.ID = "Title";
            this._titleLabel.Text = this.Title;

            this._dragPanel.Controls.Add(this._titleLabel);

            this._messageLabel = new Label();
            this._messageLabel.ID = "Message";

            this._messageLabel.Text = this.Message;

            this.Controls.Add(this._messageLabel);

            RegisterButtons();

            if (this.AllowDrag)
            {
                DragPanelExtender dragExtender = new DragPanelExtender();
                dragExtender.ID = "dragExtender";
                dragExtender.DragHandleID = this._dragPanel.ClientID;
                dragExtender.TargetControlID = this.ID;
                dragExtender.EnableClientState = false;
                dragExtender.EnableViewState = false;
                this.Extenders.Add(dragExtender);
                this.Controls.Add(dragExtender);
            }

            base.CreateChildControls();
        }

        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            if (!this.DesignMode)
            {
                writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");
                base.AddAttributesToRender(writer);
            }
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            this.EnsureChildControls();

            this._dragPanel.ApplyStyle(this.TitleStyle);
            this._dragPanel.RenderControl(writer);

            this.BodyStyle.AddAttributesToRender(writer);
            writer.AddAttribute(HtmlTextWriterAttribute.Id, String.Format("{0}_Body", this.ClientID));
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            this._messageLabel.RenderControl(writer);
            writer.RenderEndTag(); // Body DIV

            this.FooterStyle.AddAttributesToRender(writer);
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            foreach (PromptButton button in this.Buttons)
            {
                button.RenderControl(writer);
            }
            writer.RenderEndTag(); // Footer DIV

            foreach (Control extender in this.Extenders)
            {
                extender.RenderControl(writer);
            }
        }

        private void RegisterButtons()
        {
            if (this.ButtonOptions != ButtonOptions.Custom)
            {
                this.Buttons.Clear();

                switch (this.ButtonOptions)
                {
                    case ButtonOptions.Ok:
                        this.Buttons.Add(new PromptButton("ok", "ok", "Ok", false));
                        break;
                    case ButtonOptions.OkCancel:
                        this.Buttons.Add(new PromptButton("ok", "ok", "Ok", true));
                        this.Buttons.Add(new PromptButton("cancel", "cancel", "Cancel", false));
                        break;
                    case ButtonOptions.YesNo:
                        this.Buttons.Add(new PromptButton("yes", "yes", "Yes", true));
                        this.Buttons.Add(new PromptButton("no", "no", "No", false));
                        break;
                }
            }

            foreach (PromptButton button in this.Buttons)
            {
                button.SetPrompt(this);
                button.ApplyStyle(this.DefaultButtonStyle);
                this.Controls.Add(button);
            }
        }

        protected virtual void OnCommand(CommandEventArgs e)
        {
            CommandEventHandler handler = (CommandEventHandler)base.Events[Prompt.EventCommand];
            if (handler != null)
                handler(this, e);

            base.RaiseBubbleEvent(this, e);
        }

        protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            List<ScriptDescriptor> descriptors = new List<ScriptDescriptor>();

            ScriptControlDescriptor promptDescriptor = new ScriptControlDescriptor("AfterEdge.Web.UI.Prompt", this.ClientID);
            promptDescriptor.AddProperty("title", this.Title);
            promptDescriptor.AddProperty("message", this.Message);
            promptDescriptor.AddProperty("causesValidation", this.CausesValidation);
            promptDescriptor.AddProperty("validationGroup", this.ValidationGroup);
            promptDescriptor.AddProperty("postBackUrl", this.PostBackUrl);
            promptDescriptor.AddProperty("postBackTarget", this.PostBackTarget);
            promptDescriptor.AddProperty("modal", this.Modal);
            promptDescriptor.AddProperty("allowDrag", this.AllowDrag);
            promptDescriptor.AddProperty("backgroundCssClass", this.BackgroundCssClass);

            descriptors.Add(promptDescriptor);

            foreach (String key in this.ClientEvents.Handlers.Keys)
            {
                String eventHandler = this.ClientEvents.Handlers[key];
                if (!String.IsNullOrEmpty(eventHandler))
                    promptDescriptor.AddEvent(key, eventHandler);
            }

            foreach (PromptButton button in this.Buttons)
            {
                ScriptControlDescriptor buttonDescriptor = new ScriptControlDescriptor("AfterEdge.Web.UI.PromptButton", button.ClientID);
                buttonDescriptor.AddProperty("text", button.Text);
                buttonDescriptor.AddProperty("onClientClick", button.OnClientClick);
                buttonDescriptor.AddProperty("autoPostBack", button.AutoPostBack);
                buttonDescriptor.AddProperty("commandArgument", button.CommandArgument);
                buttonDescriptor.AddComponentProperty("prompt", this.ClientID);

                descriptors.Add(buttonDescriptor);
            }

            foreach (PromptTrigger trigger in this.Triggers)
            {
                ScriptComponentDescriptor triggerDescriptor = null;

                if (trigger.TargetControlType == TriggerControlType.PageControl)
                {
                    triggerDescriptor = new ScriptComponentDescriptor("AfterEdge.Web.UI.PromptElementEventExtender");
                    triggerDescriptor.AddProperty("eventName", trigger.EventName);
                    triggerDescriptor.AddComponentProperty("prompt", this.ClientID);
                }
                else
                {
                    String targetControlID = FindControlID(trigger);
                    triggerDescriptor = new ScriptBehaviorDescriptor("AfterEdge.Web.UI.PromptExtender", trigger.TargetControlID);
                    triggerDescriptor.AddProperty("eventName", trigger.EventName);
                    triggerDescriptor.AddComponentProperty("prompt", this.ClientID);
                    triggerDescriptor.AddProperty("controlType", trigger.TargetControlType);
                    triggerDescriptor.AddProperty("preventDefault", trigger.PreventDefault);
                }

                if (!String.IsNullOrEmpty(trigger.ScriptCondition))
                {
                    triggerDescriptor.AddEvent("condition", trigger.ScriptCondition);
                }

                descriptors.Add(triggerDescriptor);
            }

            return descriptors;
        }

        private String FindControlID(PromptTrigger trigger)
        {
            String targetControlID = null;

            if (trigger.TargetControlType == TriggerControlType.WebControl)
            {
                Control targetControl = this.NamingContainer.FindControl(trigger.TargetControlID);
                if (targetControl != null) targetControlID = targetControl.ClientID;
            }
            else { targetControlID = trigger.TargetControlID; }

            if (String.IsNullOrEmpty(targetControlID))
                throw new ArgumentException(String.Format("Unable to find control \"{0}\" referenced in the PromptTrigger ControlID.", trigger.TargetControlID));

            return targetControlID;
        }

        protected override IEnumerable<ScriptReference> GetScriptReferences()
        {
            List<ScriptReference> references = new List<ScriptReference>();
            references.AddRange(ScriptObjectBuilder.GetScriptReferences(this.GetType()));

            ScriptReference promptReference = new ScriptReference();
            promptReference.Assembly = this.GetType().Assembly.FullName;
            promptReference.Name = "AfterEdge.Web.UI.Prompt.ClientScripts.Prompt.js";

            references.Add(promptReference);

            return references;
        }

        #region IPostBackEventHandler Members

        void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
        {
            CommandEventArgs args = new CommandEventArgs("Prompt", eventArgument);
            OnCommand(args);
        }

        #endregion
    }
}
